#pragma once

#include "Util.hpp"
#include "online.hpp"
#include "db.hpp"
#include "chess_ai.hpp"
#include <memory>
#include <vector>

enum room_status
{
    GAMEING, //游戏中
    GAMEOVER //游戏结束
};

typedef std::shared_ptr<online_manager> online_manager_ptr;
typedef std::shared_ptr<users_table> users_table_ptr;

class room
{
public:
    room(uint32_t room_id, online_manager_ptr online_user, users_table_ptr user_table)
        : _room_id(room_id)
        , _status(GAMEING)
        , _player_count(0)
        , _online_user(online_user)
        , _user_table(user_table)
    {}

    uint32_t id() {return _room_id; }
    uint32_t player_count() { return _player_count; }
    virtual bool addPlayer(uint32_t uid) { ++_player_count; return true; }

    virtual void handleExit(uint32_t uid) = 0;
    virtual std::vector<uint32_t> getAllPlayer() = 0;
    virtual void handleRequest(Json::Value& req) = 0;
    virtual Json::Value RespondSuccess(uint32_t self_id) = 0;

    bool haveSensitiveVocabulary(std::string& masg)
    {
        return masg.find("垃圾") != std::string::npos;
    }

    // 聊天
    Json::Value handleChat(Json::Value& req)
    {
        Json::Value resp;
        resp["optype"] = "chat";

        std::string masg = req["message"].asString();
        // 查看是否存在敏感词汇
        if (haveSensitiveVocabulary(masg))
        {
            resp["result"] = false;
            resp["reason"] = "消息包含敏感词汇";
            return resp;
        }

        // 消息没有问题，广播消息
        resp["result"] = true;
        resp["room_id"] = req["room_id"].asUInt();
        resp["uid"] = req["uid"].asUInt();
        resp["message"] = masg;
        
        return resp;
    }

    // 将指定信息广播给房间内所有玩家
    void boardCast(Json::Value& req)
    {
        std::string body;
        Util::json::serialize(req, &body);
        auto pa = _online_user->get_users_from_room();
        auto it = pa.first;
        auto end = pa.second;
        while (it != end)
        {
            if (it->second.get() != nullptr)
                it->second->send(body);
            ++it;
        }
    }

protected:
    uint32_t _room_id; // 房间ID，房间号
    room_status _status; // 游戏房间状态
    uint32_t _player_count; // 玩家数量
    online_manager_ptr _online_user; //在线用户管理
    users_table_ptr _user_table; //用户表
};

class gobang_room final : public room
{
    const static uint32_t BOARD_ROW = 15;
    const static uint32_t BOARD_COL = 15;
    enum class chess 
    {
        NONE,
        WHITE,
        BLACK
    };

public:

    gobang_room(uint32_t room_id, online_manager_ptr online_user, users_table_ptr user_table)
        : room(room_id, online_user, user_table)
        , _white_id(0)
        , _black_id(0)
        , _board(BOARD_ROW, std::vector<chess>(BOARD_COL, chess::NONE))
    {}

    bool addWhitePlayer(uint32_t uid)
    {
        if (_white_id != 0)
        {
            LOG(NORMAL, "白棋用户已添加");
            return false;
        }
        else 
        {
            _white_id = uid;
            ++_player_count;
            return true;
        }
    }

    bool addBlackPlayer(uint32_t uid) 
    {
        if (_black_id != 0)
        {
            LOG(NORMAL, "黑棋用户已添加");
            return false;
        }
        else 
        {
            _black_id = uid;
            ++_player_count;
            return true;
        }
    }

    bool addPlayer(uint32_t uid) override
    {
        if (_black_id == 0)
        {
            if (!addBlackPlayer(uid))
                return false;
        }
        else if (_white_id == 0)
        {
            if (!addWhitePlayer(uid))
                return false;
        }
        return true;
    }

    Json::Value RespondSuccess(uint32_t self_id)
    {
        Json::Value resp_json;
        auto users = getAllPlayer();
        
        resp_json["optype"] = "room_ready";
        resp_json["result"] = true;
        resp_json["room_id"] = id();
        resp_json["self_id"] = self_id;
        resp_json["white_id"] = users[1];
        resp_json["black_id"] = users[0];

        return resp_json;
    }

    // 玩家退出
    void handleExit(uint32_t uid)
    {
        Json::Value resp;
        if (_status == GAMEING)
        {
            uint32_t win_uid = uid==_white_id ? _black_id : _white_id;
            uint32_t lose_uid = win_uid==_white_id ? _black_id : _white_id;
            LOG(DEBUG, "玩家退出, 游戏结束");
            resp["optype"] = "put_chess";
            resp["result"] = true; // 游戏是否结束
            resp["reason"] = "对方已掉线"; // 结束原因
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = uid;
            resp["row"] = Json::Value::nullRef;
            resp["col"] = Json::Value::nullRef;
            resp["winner"] = win_uid;
            boardCast(resp);

            _user_table->win(win_uid);
            _user_table->lose(lose_uid);

            _status = GAMEOVER;
        }

        // 防止多次退出
        if (_player_count > 0)
        {
            --_player_count;
        }
    }

    // 处理请求，更具请求调用不同的方法, 得到相应结果
    void handleRequest(Json::Value& req)
    {
        Json::Value resp;

        // 确认房间号
        if (req["room_id"].asUInt() != room::id())
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
            room::boardCast(resp);
            return;
        }

        //根据不同的请求调用不同的处理函数
        if (req["optype"].asString() == "put_chess")
        {
            resp = handleChessMove(req);
            resp["optype"] = "put_chess";

            if (resp["result"].asBool() 
                && (resp["winner"].asUInt()!=0))
            {
                uint32_t win_uid = resp["winner"].asUInt();
                uint32_t lose_uid = win_uid==_white_id ? _black_id : _white_id;
                room::_user_table->win(win_uid);
                room::_user_table->lose(lose_uid);

                _status = GAMEOVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            resp = room::handleChat(req);
        }
        else
        {
            resp["optype"] = req.asString();
            resp["result"] = false;
            resp["reason"] = "未知请求";
        }

        room::boardCast(resp);
    }

    uint32_t getWhitePlayer() { return _white_id; }
    uint32_t getBlackPlayer() { return _black_id; }
    std::vector<uint32_t> getAllPlayer() { return {_black_id, _white_id}; }

    // 下棋动作
    Json::Value handleChessMove(Json::Value& req)
    {
        Json::Value resp;

        uint32_t cur_uid = req["uid"].asUInt();
        uint32_t row = req["row"].asUInt();
        uint32_t col = req["col"].asUInt();
        if (!room::_online_user->in_game_room(cur_uid))
        { 
            uint32_t win_uid = cur_uid==_white_id ? _black_id : _white_id;
            uint32_t lose_uid = win_uid==_white_id ? _black_id : _white_id;

            resp["result"] = true; // 游戏是否结束
            resp["reason"] = "对方已掉线"; // 结束原因
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = win_uid;

            _user_table->win(win_uid);
            _user_table->lose(lose_uid);

            _status = GAMEOVER;

            return resp;
        }

        if (_board[row][col] != chess::NONE) // 无法下棋
        {
            resp["result"] = false;
            resp["reason"] = "当前位置已被占用"; // 结束原因
            return resp;
            
        }

        chess color = cur_uid==_white_id ? chess::WHITE : chess::BLACK;
        _board[row][col] = color;
        if (isWin(row, col, color))
        {
            uint32_t win_uid = color==chess::WHITE ? _white_id : _black_id;

            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = win_uid;
        }
        else 
        {
            resp["result"] = true;
            resp["reason"] = "游戏继续";
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = 0;
        }

        return resp;
    }

    bool isWin(uint32_t row, uint32_t col, chess color)
    {
        if (row >= _board.size() || col >= _board[0].size())
            return false;

        int dx[4] = {1, 0, 1, 1};
        int dy[4] = {0, 1, 1, -1};

        for (int i = 0; i < 4; ++i)
        {
            int count = 1; 
            int r = row + dx[i];
            int c = col + dy[i];

            while (r >= 0 && r < _board.size() && c >= 0 && c < _board[0].size() && _board[r][c] == color)
            {
                count++;
                r += dx[i];
                c += dy[i];
            }

            r = row - dx[i];
            c = col - dy[i];

            while (r >= 0 && r < _board.size() && c >= 0 && c < _board[0].size() && _board[r][c] == color)
            {
                count++;
                r -= dx[i];
                c -= dy[i];
            }

            if (count >= 5)
                return true;
        }

        return false;
    }


private:
    uint32_t _white_id; // 白棋用户ID
    uint32_t _black_id; // 黑棋用户ID 
    std::vector<std::vector<chess>> _board; // 棋盘
};


class train_room final : public room
{
    const static uint32_t BOARD_ROW = 15;
    const static uint32_t BOARD_COL = 15;
public:
    train_room(uint32_t room_id, online_manager_ptr online_user, users_table_ptr user_table)
        : room(room_id, online_user, user_table)
        , _uid(0)
        , _board(BOARD_ROW, std::vector<int8_t>(BOARD_COL))
    {}

    virtual bool addPlayer(uint32_t uid) override
    {
        if (_uid != 0)
            return false;
        
        _uid = uid;
        ++_player_count;    
        return true;
    }

    void handleExit(uint32_t uid) override
    {
        Json::Value resp;
        if (_status == GAMEING)
        {
            LOG(DEBUG, "玩家退出, 游戏结束");
            resp["optype"] = "put_chess";
            resp["result"] = true; // 游戏是否结束
            resp["reason"] = "对方已掉线"; // 结束原因
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = uid;
            resp["row"] = Json::Value::nullRef;
            resp["col"] = Json::Value::nullRef;
            resp["winner"] = uid + 1;
            boardCast(resp);

            _status = GAMEOVER;
        }

        // 防止多次退出
        if (_player_count > 0)
        {
            --_player_count;
        }
    }

    std::vector<uint32_t> getAllPlayer() override
    {
        return { _uid };
    }

    Json::Value RespondSuccess(uint32_t self_id)
    {
        Json::Value resp_json;
        auto users = getAllPlayer();
        
        resp_json["optype"] = "room_ready";
        resp_json["result"] = true;
        resp_json["room_id"] = id();
        resp_json["self_id"] = self_id;
        resp_json["white_id"] = self_id + 1;
        resp_json["black_id"] = self_id;

        return resp_json;
    }

    void handleRequest(Json::Value& req) override
    {
        Json::Value resp;

        // 确认房间号
        if (req["room_id"].asUInt() != room::id())
        {
            resp["optype"] = req["optype"].asString();
            resp["result"] = false;
            resp["reason"] = "房间号不匹配";
            room::boardCast(resp);
            return;
        }

        //根据不同的请求调用不同的处理函数
        if (req["optype"].asString() == "put_chess")
        {
            resp = handleChessMove(req);
            resp["optype"] = "put_chess";

            if (resp["result"].asBool() 
                && (resp["winner"].asUInt()!=0))
            {
                _status = GAMEOVER;
            }
        }
        else
        {
            resp["optype"] = req.asString();
            resp["result"] = false;
            resp["reason"] = "未知请求";
        }

        room::boardCast(resp);
    }

   // 下棋动作
   Json::Value handleChessMove(Json::Value& req)
   {
       Json::Value resp;

       uint32_t cur_uid = req["uid"].asUInt();
       uint32_t row = req["row"].asUInt();
       uint32_t col = req["col"].asUInt();
       if (!room::_online_user->in_game_room(cur_uid))
       { 
        //    uint32_t win_uid = cur_uid==_white_id ? _black_id : _white_id;
        //    uint32_t lose_uid = win_uid==_white_id ? _black_id : _white_id;

           resp["result"] = true; // 游戏是否结束
           resp["reason"] = "对方已掉线"; // 结束原因
           resp["room_id"] = room::id(); //房间ID
           resp["uid"] = cur_uid;
           resp["row"] = row;
           resp["col"] = col;
           resp["winner"] = cur_uid + 1;

           _status = GAMEOVER;

           return resp;
       }

       if (_board[row][col] != 0) // 无法下棋
       {
           resp["result"] = false;
           resp["reason"] = "当前位置已被占用"; // 结束原因
           return resp;
       }

       _board[row][col] = 'W';
       GameTree gt = GameTree(9, 2, _board);
       uint8_t result = gt.game();

       if (result == 'B') {
            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid + 1;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = cur_uid + 1; // 确保差异性
            return resp;
        }
        else if (result == 'W') {
            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = cur_uid; // 确保差异性
            return resp;
        }

        auto pos = gt.get_next_pos();
        if (pos.first != 255 && pos.second != 255) 
            _board[pos.first][pos.second] = 'B';
        else if (pos.first == 255 && pos.second == 255)
        {
            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = cur_uid; // 确保差异性
            return resp;
        }

        result = GameTree::board_identify(_board);

        if (result == 'B') {
            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid + 1;
            resp["row"] = pos.first;
            resp["col"] = pos.second;
            resp["winner"] = cur_uid + 1; // 确保差异性
            return resp;
        }
        else if (result == 'W') {
            resp["result"] = true;
            resp["reason"] = "游戏胜利"; // 结束原因 
            resp["room_id"] = room::id(); //房间ID
            resp["uid"] = cur_uid;
            resp["row"] = row;
            resp["col"] = col;
            resp["winner"] = cur_uid; // 确保差异性
            return resp;
        }

        // 游戏继续
        resp["result"] = true;
        resp["reason"] = "游戏继续";
        resp["room_id"] = room::id(); //房间ID
        resp["uid"] = cur_uid + 1;
        resp["row"] = pos.first;
        resp["col"] = pos.second;
        resp["winner"] = 0; // 确保差异性

        return resp;
   }

private:
    uint32_t _uid;
    std::vector<std::vector<int8_t>> _board; // 棋盘
};